import { deepMerge } from "deepmerge";
import { Fragment, JSX } from "preact/jsx-runtime";
import { useEffect } from "preact/hooks";
import { computed, useSignal } from "@preact/signals";
interface TableHeader<T> extends JSX.HTMLAttributes<HTMLTableCellElement> {
  render?: (col: Column<T>) => JSX.Element | string | number;
}
interface BaseColumnType<T> {
  header?: string | TableHeader<T>;
  render?: (row: T) => JSX.Element | string | number;
  selectionType?: "radio" | "checkbox";
}
/**
 * 选择列配置
 */
interface SelectionColumn<T>
  extends BaseColumnType<T>, JSX.HTMLAttributes<HTMLTableCellElement> {
  key: keyof T;
  selectionType: "radio" | "checkbox";
}
/**
 * 普通显示列配置
 */
interface NormalColumn<T>
  extends BaseColumnType<T>, JSX.HTMLAttributes<HTMLTableCellElement> {
  key?: keyof T;
}
/**
 * 表格列配置
 */
export type Column<T> = SelectionColumn<T> | NormalColumn<T>;
export interface ActionButtonsProps
  extends JSX.HTMLAttributes<HTMLButtonElement> {
}
/**
 * 标准表格组件
 */
export interface STDTableProps<T = object> /*
  extends JSX.HTMLAttributes<HTMLTableElement> */ {
  class?: string;
  columns: Column<T>[];
  tableList?: T[];
  // data?: T[];
  actions?: JSX.Element[] | JSX.Element;
  /**
   * 选择事件
   * @param rows 选中的行
   * @param selectAll 是否全选
   */
  onSelect?: (
    rows?: Array<T>, /* colIndex: number,  */
    // 歧义:如果只是单表不会跨分页选择,则实际上不需要传入,因为可以从外部判断是否宣布选中,
    // 如果是需要跨分页选中,则该参数实际上不适用,因为不知道跨分页选中了多少
    selectAll?: boolean,
  ) => void;
  onsearch?: (searchValue: string) => void;
}

export function STDTable<T = object>(
  { class: OC, columns, tableList, actions, onSelect, onsearch }: STDTableProps<
    T
  >,
) {
  // 已经选中的主键集合
  const choosedSet = useSignal(new Set());
  // 出现变化,将选中集合清空
  useEffect(() => {
    if (!onSelect) return;
    choosedSet.value.clear();
  }, [tableList]);
  // 提示可能存在的多个选择列
  useEffect(() => {
    if (!columns.length) {
      return;
    }
    const mutiSelectionCol = columns.filter((i) => i.selectionType).length > 1;
    if (mutiSelectionCol) {
      console.warn("STDTable: 存在多个选择列,请确认配置是否正确");
    }
  }, [columns]);
  /**
   * 全选处理器
   * todo: 优化性能,选中应该只有一个,但是在实际绑定时,绑定了多个,可能出现多个表头可选的情况
   * @param e
   * @returns
   */
  const selectAllHandler = (
    e: JSX.TargetedEvent<HTMLInputElement, InputEvent>,
    // colIndex: number,
  ) => {
    if (!onSelect) return;
    if (!e.currentTarget) return;
    const { checked, dataset } = e.currentTarget;
    if (!dataset.key) {
      return;
    }
    const propsKey = dataset["key"] as keyof T;
    if (!checked) {
      choosedSet.value = new Set();
    } else {
      choosedSet.value = new Set(tableList?.map((i) => i[propsKey!]));
    }
    onSelect(
      checked ? tableList ?? [] : [],
      // colIndex,
      e.currentTarget.checked,
    );
  };
  const rowSelectHandler = (
    e: JSX.TargetedEvent<HTMLInputElement, InputEvent>,
  ) => {
    if (!onSelect) return;
    const { checked, value, dataset = {}, type } = e.currentTarget;
    const propsKey = dataset["key"] as keyof T;
    if (type === "radio") {
      onSelect(
        tableList?.filter((i) => value === (i[propsKey])),
        false,
      );
      return;
    }
    const currentSet = new Set(choosedSet.value);

    if (checked) {
      currentSet.add(value);
    } else {
      currentSet.delete(value);
    }
    if (!dataset.key) {
      return;
    }

    // const propKey= dataset.key??dataset.col;
    // const propValue= propKey ? String(row[propKey] ?? "") : "";
    onSelect(
      tableList?.filter((i) => currentSet.has(i[propsKey])),
      currentSet.size === tableList?.length,
      //  Array.from(choosedSet.value.values()).map((i) =>)
    );
    choosedSet.value = currentSet;
  };
  const actionDom = computed(() => {
    if (!actions) return null;
    return (
      <div className="relative group">
        <button
          class="inline-flex items-center text-gray-500 bg-white border border-gray-300 focus:outline-none hover:bg-gray-100 font-medium rounded-lg text-sm px-3 py-1.5 dark:bg-gray-800 dark:text-gray-400 dark:border-gray-600 dark:hover:bg-gray-700 dark:hover:border-gray-600 "
          type="button"
        >
          <span class="sr-only">Action button</span>
          操作
          <svg
            class="w-2.5 h-2.5 ms-2.5"
            aria-hidden="true"
            xmlns="http://www.w3.org/2000/svg"
            fill="none"
            viewBox="0 0 10 6"
          >
            <path
              stroke="currentColor"
              stroke-linecap="round"
              stroke-linejoin="round"
              stroke-width="2"
              d="m1 1 4 4 4-4"
            />
          </svg>
        </button>
        {/* 菜单下拉框 */}
        <div
          id="dropdownAction"
          class="z-10 top-9 left-0  hidden group-focus-within:block absolute divide-y divide-gray-100 rounded-lg shadow w-max bg-white dark:bg-gray-700  dark:divide-gray-600"
        >
          {Array.isArray(actions)
            ? (
              <ul
                class="py-1 text-sm text-gray-700 dark:text-gray-200"
                aria-labelledby="dropdownActionButton"
              >
                {actions?.map((item) => <li>{item}</li>)}
              </ul>
            )
            : actions}
        </div>
      </div>
    );
  });
  const searchDom = computed(() => {
    if (!onsearch) return null;
    return (
      <Fragment>
        <label for="table-search" class="sr-only">Search</label>
        <div class="relative">
          <div class="absolute inset-y-0 rtl:inset-r-0 start-0 flex items-center ps-3 pointer-events-none">
            <svg
              class="w-4 h-4 text-gray-500 dark:text-gray-400"
              aria-hidden="true"
              xmlns="http://www.w3.org/2000/svg"
              fill="none"
              viewBox="0 0 20 20"
            >
              <path
                stroke="currentColor"
                stroke-linecap="round"
                stroke-linejoin="round"
                stroke-width="2"
                d="m19 19-4-4m0-7A7 7 0 1 1 1 8a7 7 0 0 1 14 0Z"
              />
            </svg>
          </div>
          <input
            type="text"
            id="table-search"
            class="block pt-2 ps-10 text-sm text-gray-900 border border-gray-300 rounded-lg w-80 bg-gray-50 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
            placeholder="Search for users"
          />
        </div>
      </Fragment>
    );
  });
  const topActions = computed(() => {
    if (!(searchDom.value || actionDom.value)) return null;
    return (
      <div class="flex  justify-between items-center xs:items-start xs:flex-col flex-wrap xs:space-y-4 space-y-0 py-4 ">
        {/* 操作按钮 */}
        {actionDom}
        {/* 右边 */}
        {searchDom}
      </div>
    );
  });
  /**
   * 格式化className
   */
  const className = `relative overflow-x-auto shadow-md xs:rounded-lg ${
    OC ?? ""
  }`;
  return (
    <div class={className}>
      {/* 顶部操作栏 */}
      {topActions}
      <table class="w-full text-sm text-left rtl:text-right text-gray-500 dark:text-gray-400">
        <thead class="text-xs text-gray-700 uppercase bg-gray-50 dark:bg-gray-700 dark:text-gray-400">
          <tr>
            {columns.map((col, index) => {
              const { header, render: _, selectionType, key, ...ops } = col;
              if (!header || typeof header === "string") {
                return (
                  <th
                    key={index}
                    scope="col"
                    className={`${
                      selectionType === "checkbox" ? "p-4" : "px-6 py-3"
                    }`}
                    {...ops}
                  >
                    {selectionType === "checkbox" && (
                      <Fragment>
                        <input
                          id="checkbox-all-search"
                          onChange={selectAllHandler}
                          disabled={!tableList?.length}
                          checked={(choosedSet.value.size ===
                            tableList?.length) &&
                            !!tableList?.length}
                          data-key={key}
                          class="w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800 dark:focus:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600"
                          type="checkbox"
                        />
                        <label for="checkbox-all-search" class="sr-only">
                          checkbox
                        </label>
                      </Fragment>
                    )}
                    {header}
                  </th>
                );
              }

              const { render: HR, title, ...hops } = header;
              return (
                <th
                  key={index}
                  scope="col"
                  {...deepMerge(ops, hops, { title, class: "px-6 py-3" })}
                >
                  {HR ? HR({ ...col }) : title}
                </th>
              );
            })}
          </tr>
        </thead>
        <tbody>
          {tableList?.map((row, rowIndex) => {
            return (
              <tr class="bg-white border-b dark:bg-gray-800 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-600">
                {columns.map((column, colIndex) => {
                  const { key, render, selectionType, ...ops } = column;
                  if (render) {
                    return (
                      <td key={key} class="px-6 py-4" {...ops}>
                        {render(row)}
                      </td>
                    );
                  }
                  const propValue = key ? String(row[key] ?? "") : "";
                  if (selectionType && key) {
                    return (
                      <td key={propValue} class="w-4 p-4" {...ops}>
                        <input
                          class="w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800 dark:focus:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600"
                          type={selectionType}
                          onChange={rowSelectHandler}
                          data-key={key}
                          id={propValue}
                          data-col={colIndex}
                          data-row={rowIndex}
                          checked={choosedSet.value.has(propValue)}
                          value={propValue}
                          name={key as string}
                        />
                        <label for={propValue} class="sr-only">
                          checkbox
                        </label>
                      </td>
                    );
                  }
                  return (
                    <td
                      key={key}
                      class="px-6 py-4 font-medium text-gray-900 whitespace-nowrap dark:text-white"
                      {...ops}
                    >
                      {key ? String(row[key] ?? "-") : ""}
                    </td>
                  );
                })}
              </tr>
            );
          })}
        </tbody>
      </table>
    </div>
  );
}

export default STDTable;
